//========================================================//
// Simple Secure Protocol Classes                         //
// Authors: Bezdolnyj Alexey aka BAM  bamalex@ngs.ru      //
//          Krechetov Anton aka kae   kae@gorodok.net     //
// NSU FIT September 2004                                 //
//========================================================//

#ifndef _SIMPLE_SECURE_PROTOCOL_
#define _SIMPLE_SECURE_PROTOCOL_

#include "../osocklib/sockstack.h"
#include "../icrypto/icrypto.h"
#include <queue>

//#include "c:\\documents and settings\\sfaktorovich\\My Documents\\remoteconsole2\\vld.h"

namespace BAM
{

	class cSSPPacket: public iPacketWrapper, public cPacket
	{
		c3DES_ENCTransform *vEnc;
		c3DES_DECTransform *vDec;
		cHMAC_MD5Transform *vSign;
		bool vSign_set;

	public:
		cSSPPacket();
		cSSPPacket(c3DES_ENCTransform &Enc, c3DES_DECTransform &Dec, cHMAC_MD5Transform &Sign);
		cSSPPacket(const cSSPPacket &P);
		cSSPPacket(const cPacket &P, c3DES_ENCTransform &Enc, c3DES_DECTransform &Dec, cHMAC_MD5Transform &Sign);
		cSSPPacket(const cPacket &P);
		qword getIntField(dword Index) const;
		void setIntField(dword Index, qword Val);
		dword getIntFieldCount() const;

		cPacket getBinField(dword Index) const;
		dword getBinFieldLength(dword Index) const;
		void setBinField(dword Index, const cPacket &P);
		dword getBinFieldCount() const;

		dword getHeaderLength() const;
		dword getFullLength(const cPacket &P) const;
		dword getMaxLength() const;

		cPacket compile();
		iPacketWrapper* decompile(const cPacket &P);
	};

	class cSspController;

	typedef sdword SspState;
	typedef byte SspPacketType;
	//$ added. Analog PrpRetCode
	typedef byte SspRetCode;

	const SspPacketType ConnectPacket = 0;
	const SspPacketType ConnAccPacket = 1;
	const SspPacketType ResetPacket = 2;
	const SspPacketType ConnBadPacket = 3;
	const SspPacketType DataPacket = 4;
	const SspPacketType DisconnectPacket = 5;

	//$ Normal connect packet
	const SspRetCode Ssp_Ret_Normal = 0x00;
	//$ Bad sign in connect packet
	const SspRetCode Ssp_Ret_BadSign = 0x01;
	//$ Bad login in connect packet
	const SspRetCode Ssp_Ret_BadLogin = 0x02;
	//$ Connect packet is wrong
	const SspRetCode Ssp_Ret_BadPacket = 0x03;

	const SspState SCState_Error = -2;
	const SspState SCState_BadPacket = -1;
	const SspState SCState_None = 0;//after creation
	const SspState SCState_Initializing = 1;//when initializing - Connect-ConnectAcc2 packet
	const SspState SCState_Connecting = 2;//ConnectAcc
	const SspState SCState_Resetting = 3;//when resetting - Reset-ResetAcc packet
	const SspState SCState_Normal = 4;//when is ready to send data
	const SspState SCState_Finished = 5;//when connection is successfully finished

	const dword SspRndLen = 4;
	const dword SspByte64k = 65536;
	const dword SspHashLen = 16;//MD5hashLen;

	const word Ssp_PTypeLen = 1;
	const word Ssp_OutCountLen = 2;
	const word Ssp_InCountLen = 2;
	const word Ssp_LengthLen = 2;
	const word Ssp_ReservedLen = 1;
	const word Ssp_HeaderLen = 8;
	const word Ssp_TailLen = SspHashLen;

	class SspException: public cException
	{
	public:
		SspException(const char *Msg)
			:cException(Msg)
		{}
	};

	const SspException EXC_SspHash("Packet sign is invalid");
	const SspException EXC_SspSize("Packets longer than 65527b are not supported");

	class cSspController : public cAssemblerController
	{
	private:
		cPacket vInitLogin;
		cLoginManager* vLoginManager;
		queue<cPacket> vIncomingQ;
		queue<cPacket> vOutcomingQ;
		dword vRnd;
		SspState vState;
		cDHManager* vDH;

		word vICounter;
		word vOCounter;
		c3DES_ENCTransform vEnc;
		c3DES_DECTransform vDec;
		cHMAC_MD5Transform vSign;

		void outPacket(cSSPPacket &P);
		void resetKey(const cPacket &Agree);

		SspRetCode RcvConnectPacket(cPacket &Data);
		void RcvConnectBadPacket(SspRetCode ret);
		void RcvConnAccPacket(cPacket &Data);
		void RcvResetPacket(cPacket &Data);

	protected:
		void received(const iPacketWrapper &P);

	public:
		cSspController(cLoginManager* lm, cPacket Login);
		~cSspController();
		void onConnect(const cIPAddr &Addr);
		void onDisconnect();
		void onInit(const cIPAddr &Addr);
		void onSend(const cPacket &P);
		void onRecv(const cPacket &P);
		bool haveOutcome() const;
		bool haveIncome() const;
		bool isInited() const;
		bool isFinished() const;
		Sock_State state(Sock_State TransportState) const;
		cPacket outcome();
		cPacket income();
		iPacketController* clone() const;
	};

	class cSSPSocket : public cPSocket
	{
	public:
		cSSPSocket(iSocket* trnsp, cLoginManager *lm, const cPacket &login)
			:cPSocket(new cSspController(lm, login), trnsp)
		{}

		bool RecvEx(cPacket *p, bool block = false);
		int RecvLen(unsigned char *buf, int len, bool block = false);
	};
}
#endif
